package matrix;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import org.apache.log4j.Logger;

/**
 * Created with IntelliJ IDEA.
 * User: aleks_000
 * Date: 29.09.13
 * Time: 16:35
 * To change this template use File | Settings | File Templates.
 */
public final class ArrayListMatrix extends BaseMatrix {
    private ArrayList<Double> source;
    private final static Logger LOGGER = Logger.getLogger(ArrayListMatrix.class);

    // static nested class which implements constructor
    private static class ArrayListMatrixFactory implements MatrixFactory<ArrayListMatrix> {
        @Override
        public ArrayListMatrix create(int rowCount, int columnCount) {
            return new ArrayListMatrix(rowCount, columnCount);
        }
    }

    private ArrayListMatrix(int rowCount, int columnCount) {
        super(rowCount, columnCount);
        int size = rowCount * columnCount;
        source = new ArrayList<Double>(size);
    }

    public double getValue(int row, int column) {
        if (row < 0 || row >= rowCount) {
            throw new ArrayIndexOutOfBoundsException();
        }
        if (column < 0 || column >= columnCount) {
            throw new ArrayIndexOutOfBoundsException();
        }
        return source.get(columnCount * row + column);
    }

    public void setValue(int row, int column, double value) {
        if (row < 0 || row >= rowCount) {
            throw new ArrayIndexOutOfBoundsException();
        }
        if (column < 0 || column >= columnCount) {
            throw new ArrayIndexOutOfBoundsException();
        }
        source.add(columnCount * row + column, value);
    }

    @Override
    public Matrix multiply(Matrix matrix, MatrixMultiplier multiplier)
            throws MatrixIncompatibleException {
        LOGGER.info(String.format("Multiply current matrix (%s) and %s matrix",this.toString(), matrix.toString()));
        return multiply(this, matrix, multiplier, new ArrayListMatrixFactory());
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        LOGGER.info("Start write data in the ObjectOutputStream");
        for (Double value : source) {
            out.writeDouble(value);
        }
        LOGGER.info("All data are written");
    }

    private void readObject(ObjectInputStream in) throws IOException {
        int size = rowCount*columnCount;
        LOGGER.info(String.format("Start read %d from ObjectInputStream", size));
        source = new ArrayList<Double>(size);
        for (int i = 0; i < size; ++i) {
            source.add(in.readDouble());
        }
        LOGGER.info("All data are read");

    }

    public static ArrayListMatrix parse(String str) {
        return parse(str, new ArrayListMatrixFactory());
    }

    public static ArrayListMatrix createIdentity(int size) {
        LOGGER.info(String.format("Create identity array list matrix with size %d", size));
        return createIdentity(size, new ArrayListMatrixFactory());
    }

    public static ArrayListMatrix createRandom(int rowCount, int columnCount) {
        LOGGER.info(String.format("Create random array list matrix with %d rows and %d columns", rowCount, columnCount));
        return createRandom(rowCount, columnCount, new ArrayListMatrixFactory());
    }

    public static ArrayListMatrix create(int rowCount, int columnCount) {
        LOGGER.info(String.format("Create empty array list matrix with %d rows and %d columns", rowCount, columnCount));
        return create(rowCount, columnCount, new ArrayListMatrixFactory());
    }
}
